﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace PPIDE
{
    class PpSim
    {
        private static int[] R = new int[8];
        private static bool ZF, SF,OF ;
        private static int[] Mem = new int[4096];//暂定认为mem为4K*32bit=16KB
        private enum Status{AOK,HLT,ADRERR,INSERR};
        private static Status CPU_Staus;
        private static Int16 next_instr_pc;//每次增加4
        private static int steps;
        public static int MAX_STEPS=10*1000;
        private static void initCPU()
        {
            next_instr_pc = 0;
            steps = 0;
            CPU_Staus = Status.AOK;
            for (int i = 0; i < R.Length; i++)
            {
                R[i] = 0;
            }
            for (int i = 0; i < Mem.Length; i++)
            {
                Mem[i] = 0;
            }
        }
        private static void executeInstr(InfoStruct Instr, bool is_PP_Ins, InfoStruct Ra, InfoStruct Rb, InfoStruct Rc, Int16 Imm)
        {
            if (CPU_Staus != Status.AOK)
                return;
            next_instr_pc += 4 ;
            switch (Instr.Name)
            {
                case "HALT": CPU_Staus=Status.HLT; break;
                case "NOP": return; 
                case "IRMOV16": R[Rb.Val] = Imm; break;
                case "RMMOV16": Mem[R[Rb.Val] +Imm]=(int)(Mem[R[Rb.Val] +Imm]&0xFFFF0000) | (int)(R[Ra.Val] &0x0000FFFF); break;
                case "RMMOV32": Mem[R[Rb.Val] + Imm] = R[Ra.Val]; break;
                case "MRMOV16": R[Rb.Val] = (int)(R[Rb.Val] & 0xFFFF0000) | (int)(Mem[R[Ra.Val] + Imm] & 0x0000FFFF); break;
                case "MRMOV32": R[Rb.Val] = Mem[R[Ra.Val] + Imm]; break;
                case "RRMOV16": R[Rb.Val] = (int)(R[Rb.Val] & 0xFFFF0000) | (int)(R[Ra.Val] & 0x0000FFFF); break;
                case "RRMOV32": R[Rb.Val] = R[Ra.Val]; break;

                case "ADD": R[Rc.Val] = R[Ra.Val] + R[Rb.Val]; 
                                        ZF = (R[Rc.Val] == 0); 
                                        SF=(R[Rc.Val] < 0);
                                        OF = false; break;//这里要设置flag，这里假设都没有overflow
                case "SUB": R[Rc.Val] = R[Ra.Val] - R[Rb.Val];
                                        ZF = (R[Rc.Val] == 0);
                                        SF = (R[Rc.Val] < 0); OF = false; break;
                case "MULT": R[Rc.Val] = (Int16)R[Ra.Val] * (Int16)R[Rb.Val]; 
                                        ZF = (R[Rc.Val] == 0);
                                        SF = (R[Rc.Val] < 0); OF = false; break;

                case "RET":  break;
                case "CALL":  break;
                case "PUSH": break;
                case "POP":  break;

                case "JMP": next_instr_pc=Imm; break;
                case "JMPLE": next_instr_pc = ZF || SF ? Imm : next_instr_pc; break;
                case "JMPL": next_instr_pc = !ZF || SF ? Imm : next_instr_pc; break;
                case "JMPE": next_instr_pc = ZF  ? Imm : next_instr_pc; break;
                case "JMPNE": next_instr_pc = !ZF ? Imm : next_instr_pc; break;
                case "JMPGE": next_instr_pc = ZF || !SF ? Imm : next_instr_pc; break;
                case "JMPG": next_instr_pc = !ZF || !SF ? Imm : next_instr_pc; break;

                case "BUB": return; 
                default: break;
            }
          
        }
        private static string outputStepResult(int pc)
        {
            string str_Result = string.Format("{0} steps,PC= 0x{1:X8},Status= {2:X},CC= {3}{4}{5}\n", steps, pc, CPU_Staus,
                ZF?1:0,SF?1:0,OF?1:0);
            for (int i = 0; i < R.Length; i++)
            {
                str_Result += string.Format("R{0} = 0x{1:X8}\n",i,R[i]);
            }
            for (int i = 0; i < Mem.Length; i++)
            {
                if (Mem[i] != 0)
                {
                    str_Result += string.Format("Mem 0x{0:X8}: 0x{1:X8}\n", i, Mem[i]);
                }
            }
            return str_Result;
        }
        
        private static void decodeInstr(ref Byte[] bytes, int curr_ins_indx,out InfoStruct Instr,out bool is_PP_Ins, out InfoStruct Ra, out InfoStruct Rb, out InfoStruct Rc, out Int16 Imm)
        {
            is_PP_Ins =((bytes[curr_ins_indx] & (byte)0x80)== (byte)0x80);
           
            int new_opbyte=bytes[curr_ins_indx] &0x7F;
            bool Ins_found=false;
            Instr = PpCompiler.HALT;
            for (int i = 0; i < PpCompiler.INS.Length; i++)
            {
                if (new_opbyte == PpCompiler.INS[i].Val)
                {
                    Instr = PpCompiler.INS[i];
                    Ins_found = true;
                    break;
                }
            }
            if (!Ins_found)
                throw new Exception(string.Format("Error:Bad Opcode,curr_ins_indx:{0},byte0:0x{1:X2},byte1:0x{2:X2},byte2:0x{3:X2},byte3:0x{4:X2}",curr_ins_indx,bytes[curr_ins_indx],
                    bytes[curr_ins_indx+1],bytes[curr_ins_indx+2],bytes[curr_ins_indx+3]));
            int r_indx=bytes[curr_ins_indx+1]>>4;
            Ra = r_indx < 8 ? PpCompiler.REGS[r_indx] : PpCompiler.RNULL;
            r_indx = bytes[curr_ins_indx + 1] &0x0F;
            Rb = r_indx < 8 ? PpCompiler.REGS[r_indx] : PpCompiler.RNULL;
            r_indx = bytes[curr_ins_indx + 2] >> 4;
            Rc = r_indx < 8 ? PpCompiler.REGS[r_indx] : PpCompiler.RNULL;
            Imm = (Int16)((bytes[curr_ins_indx + 2] << 8) + bytes[curr_ins_indx + 3]);
        }

        public static string Execute(ref Byte[] bytes)
        {
            string result = string.Empty;
            initCPU();
            InfoStruct Instr;
            bool is_PP_Ins;
            InfoStruct Ra,Rb,Rc;
            Int16 Imm;
            int pc;
            while (CPU_Staus == Status.AOK && steps < MAX_STEPS)
            {
               
                decodeInstr(ref  bytes, next_instr_pc, out  Instr, out  is_PP_Ins, out  Ra, out  Rb, out  Rc, out  Imm);
                pc = next_instr_pc;
                executeInstr(Instr, is_PP_Ins, Ra, Rb, Rc, Imm);//执行完后next_instr_pc++
                steps++;
                 result += outputStepResult(pc);
                 if (next_instr_pc >= bytes.Length)
                 {
                     result += "指令流已执行完，但是未遇到HALT指令";
                     break;
                 }
            }
            result+=(steps > MAX_STEPS )?("指令执行次数大于MAX:"+MAX_STEPS+"，因此执行停止。"):string.Empty;
            return result;
        }

    }
}
